package middleware

import (
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"time"
)

// 声明一个日志实例，用于实际写入日志
var sugarLogger *zap.SugaredLogger

func Logger() gin.HandlerFunc {
	initLogger() //初始化日志配置
	return func(c *gin.Context) {
		//1.记录请求开始时的信息（访问路径）
		sugarLogger.Debugf("正在尝试访问:%s", c.Request.URL.Path)
		//2.放行请求，执行后续的控制器逻辑
		c.Next()
		//3.记录请求结束后的信息（状态码，错误等）
		statusCode := c.Writer.Status()
		//3.1 若请求有错误，记录错误信息
		if len(c.Errors) > 0 {
			sugarLogger.Errorf("错误信息：%s", c.Errors.String())
		}
		//3.根据状态码记录不同级别的日志
		//Zap 会将日志内容（如 sugarLogger.Infof("请求正常：%d", statusCode) 中的字符串）存储到 MessageKey 所指定的字段中。
		if statusCode >= 400 {
			sugarLogger.Warnf("客户端警告：%d", statusCode) //4xx客户端错误
		} else if statusCode >= 500 {
			sugarLogger.Errorf("服务器错误：%d", statusCode) //5xx服务器错误
		} else {
			sugarLogger.Infof("请求正常：%d", statusCode) //2xx成功
		}

	}
}
func initLogger() {
	encoder := getEncoder()       //获取日志编码器（定义日志格式）
	writeSyncer := getLogWriter() //获取日志输出目标
	//创建zap核心组件：绑定编码器，输出目标，日志级别
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
	//创建zap日志实例，开启调用者信息（记录日志所在的文件和行号）
	//zap.AddCaller()开启调用者信息，日志会包含记录日志的代码所在的文件和行号
	logger := zap.New(core, zap.AddCaller())
	//转化为Sugaredlogger以便使用格式化接口
	sugarLogger = logger.Sugar()

}

// 日志编码器
func getEncoder() zapcore.Encoder {
	//基于生产环境配置初始化
	config := zap.NewProductionEncoderConfig()
	//配置时间格式：将时间格式化为 "2006-01-02 15:04:05"（Go 语言的参考时间格式）
	//参数说明：
	//t time.Time：需要编码的时间对象
	//enc zapcore.PrimitiveArrayEncoder:Zap提供的编码器接口，用于将数据写入日志字段
	config.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		//t.Format("2006-01-02 15:04:05") 是 Go 语言中格式化时间的标准写法
		enc.AppendString(t.Format("2006-01-02 15:04:05"))
	}
	//将日志级别显示中文
	//level zapcore.Level:表示当前日志级别
	//enc zapcore.PrimitiveArrayEncoder：Zap 提供的编码器接口，通过其 AppendString 方法将转换后的文本写入日志的 “级别” 字段。
	config.EncodeLevel = func(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
		switch level {
		case zapcore.DebugLevel:
			enc.AppendString("调试")
		case zapcore.InfoLevel:
			enc.AppendString("信息")
		case zapcore.WarnLevel:
			enc.AppendString("警告")
		case zapcore.ErrorLevel:
			enc.AppendString("错误")
		case zapcore.PanicLevel:
			enc.AppendString("恐慌")
		case zapcore.FatalLevel:
			enc.AppendString("致命")
		default:
			enc.AppendString(level.String())
		}
	}
	//配置日志字段名修该为中文（如"消息"代替默认的“msg”）
	config.MessageKey = "消息"
	config.CallerKey = "位置"
	config.TimeKey = "时间"
	config.StacktraceKey = "堆栈"
	config.LevelKey = "级别"
	//返回JSON格式的编码器（日志最终以JSON格式输出）
	return zapcore.NewJSONEncoder(config)

}

// 日志输出位置
func getLogWriter() zapcore.WriteSyncer {
	//配置lumberjack日志轮转参数
	lumberjackLogger := &lumberjack.Logger{
		Filename:   "./log/log.log", //日志文件路径（当前目录下的log文件夹）
		MaxSize:    10,              //单个日志文件的最大大小（单位MB）
		MaxBackups: 5,               //最多保留的备份文件数
		MaxAge:     30,              //日志文件的最大保存天数
		Compress:   true,            //是否压缩备份文件（节省磁盘空间）
	}
	//将lumberjack转换为Zap可识别额输出目标
	return zapcore.AddSync(lumberjackLogger)
}
